package ai.esc.speedy.admin.user.domain.querymodel;

import ai.esc.speedy.admin.user.api.*;
import ai.esc.speedy.admin.user.domain.aggregate.AppUserAggregate;
import ai.esc.speedy.admin.user.domain.valueobject.SpeedyAppUserData;
import ai.esc.speedy.admin.user.infrastructure.jwt.JwtUserInfo;
import ai.esc.speedy.admin.user.infrastructure.jwt.JwtUtil;
import ai.esc.speedy.common.constant.CommonConstants;
import ai.esc.speedy.common.util.RedisUtil;
import ai.esc.speedy.common.util.mail.MailDto;
import ai.esc.speedy.common.util.mail.MailService;
import ai.esc.speedy.common.util.SageStrUtil;
import ai.esc.speedy.common.util.id.UIDGenerator;
import ai.esc.speedy.common.valueobject.Extend;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.util.RandomUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.axonframework.commandhandling.gateway.CommandGateway;
import org.axonframework.common.jpa.EntityManagerProvider;
import org.axonframework.config.ProcessingGroup;
import org.axonframework.eventhandling.EventHandler;
import org.axonframework.modelling.command.Repository;
import org.axonframework.queryhandling.QueryHandler;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
import javax.transaction.Transactional;
import java.util.*;

/**
 * @author Rubin
 * @version v1 2020/3/4 3:51
 */
@Slf4j
@Service
@ProcessingGroup("user-service")
public class UserServiceEventHandler {

    @Resource
    private JwtUtil jwtUtil;

    @Resource
    private RedisUtil redisUtil;

    @Resource
    protected MailService mailService;

    @Resource
    protected UIDGenerator uidGenerator;

    @Resource
    protected CommandGateway commandGateway;

    @Resource
    private EntityManagerProvider entityManagerProvider;

    private EntityManager getEntityManager() {
        return entityManagerProvider.getEntityManager();
    }


    @EventHandler
    public void event(CreateAppUserEvent event) throws Exception {
        this.event(new UpdateAppUserEvent(event.getAppUser(), event.getPassport()));
    }


    @EventHandler
    @Transactional(rollbackOn = Exception.class)
    public void event(UpdateAppUserEvent event) throws Exception {
        AppUser appUser = event.getAppUser();
        Passport passport = event.getPassport();

        // ### update appUser
        AppUser oldAppUser = this.getEntityManager().find(AppUser.class, appUser.getUserId());

        if (oldAppUser != null) {
            log.info("用户账号更新...");

            // 拷贝非null值
            BeanUtil.copyProperties(appUser, oldAppUser, CopyOptions.create().ignoreNullValue());
            // merge
            this.getEntityManager().merge(oldAppUser);
            passport.setFdId(oldAppUser.getPassportId());
        } else {
            // add
            log.info("注册新账号...");
            appUser.setPassportId(passport.getFdId());
            this.getEntityManager().persist(passport);
            this.getEntityManager().persist(appUser);
            this.getEntityManager().flush();

            return;
        }

        log.info("底层账号更新...");
        // ### update passport
        Passport oldPassport = this.getEntityManager().find(Passport.class, passport.getFdId());

        if (oldPassport == null) {
            throw new Exception("系统用户不存在！");
        }
        // 拷贝非null值
        BeanUtil.copyProperties(passport, oldPassport,
                CopyOptions.create().ignoreNullValue()
                        .setIgnoreProperties("fdId",
                                "account", "createdAt"));
        // merge
        this.getEntityManager().merge(oldPassport);
        this.getEntityManager().flush();

    }

    @EventHandler
    public void event(CreatePermissionEvent event) {
        System.out.println("CreatePermissionEvent");
    }

    @EventHandler
    public void event(UpdatePermissionEvent event) {
        System.out.println("UpdatePermissionEvent");
    }


    @Resource
    private Repository<AppUserAggregate> repository;


    @QueryHandler
    public AppUserTokenResponse query(LoginQuery query) {
        Map<String, String> token = new HashMap<>(4);

        // 手机登录时username为手机号，password为短信验证码
        String username = query.getUsername();
        String password = query.getPassword();

        // 设置手机号
        Passport passport = new Passport();
        passport.setAccount(username);

        AppUser appUser = new AppUser(uidGenerator.getIdStr());

        // 短信校验
        log.info("短信校验：" + password);

        // 查找App用户信息
        TypedQuery<AppUser> appUserTypedQuery = getEntityManager()
                .createNamedQuery("AppUser.findByAccount", AppUser.class);
        appUserTypedQuery.setParameter("account", username);

        Optional<AppUser> optionalAppUser = appUserTypedQuery.getResultStream().findAny();

        if (optionalAppUser.isPresent()) {
            appUser = optionalAppUser.get();
            Extend extend = Objects.requireNonNull(appUser.getData());
            String account = String.valueOf(extend.getExtendJsonData().get("account"));
            JwtUserInfo userInfo = new JwtUserInfo(account,
                    appUser.getUserId(),
                    extend.getExtendData(),
                    appUser.getPassportId()
            );

            try {
                // 新AccessToken
                token = jwtUtil.getToken(userInfo);
            } catch (Exception e) {
                log.error("新建token错误！" + e.getLocalizedMessage());
            }

            // RefreshToken存缓存 // todo 该独立出来吗？？？
            return getAppUserTokenResponse(token);
        }

        // 发送注册命令，密码为null系统默认为自动注册
        SpeedyAppUserData userData = new SpeedyAppUserData();
        userData.setAccount(username);
        userData.setName(SageStrUtil.keepAndOmit(username));
        userData.setEmail(username.contains("@") ? username : "");
        userData.setPhone(!username.contains("@") ? username : "");

        try {
            log.info("创建用户");
            commandGateway.send(new CreateAppUserCommand(
                    Objects.requireNonNull(appUser.getUserId()), query.getAppId(),
                    JSON.parseObject(JSON.toJSONString(userData)), null, username)).join();

            try {

                JwtUserInfo userInfo = new JwtUserInfo(
                        userData.getAccount(),
                        appUser.getUserId(),
                        JSON.toJSONString(userData),
                        null
                );
                // 新AccessToken
                token = jwtUtil.getToken(userInfo);
            } catch (Exception e) {
                log.error("新建token错误！" + e.getLocalizedMessage());
            }

        } catch (Exception e) {
            log.error("创建用户失败！" + e.getLocalizedMessage());
        }

        // RefreshToken存缓存 // todo 该独立出来吗？？？
        return getAppUserTokenResponse(token);
    }

    @NotNull
    private AppUserTokenResponse getAppUserTokenResponse(Map<String, String> token) {
        log.info("RefreshToken存缓存...");
        String accessToken = token.get("access_token");
        String refreshToken = token.get("refresh_token");
        redisUtil.set(CommonConstants.REDIS_REFRESH_TOKEN_KEY +
                accessToken.substring(accessToken.lastIndexOf(".") + 1), refreshToken);

        return new AppUserTokenResponse(token.get("access_token"), token.get("refresh_token"));
    }

    @QueryHandler
    public SpeedyAppUserData query(AppUserInfoQuery query) throws Exception {
        log.info("获取用户信息");
        //JwtUserInfo userInfo = jwtUtil.getJwtUserInfo(query.getToken());

        SpeedyAppUserData userData = new SpeedyAppUserData();

//        String account = userInfo.getAccount();
//        JSONObject jsonObject = userInfo.getUserDataJSONObject();

        userData.setAccount("132465");
        userData.setName("Juu");
        userData.setAvatar_url("http://pic1.win4000.com/pic/4/1f/6d115e97ca.jpg");
        userData.setBalance("200");
        userData.setCertification_status("已认证");
        userData.setCoupon_number("3");
        userData.setPlatform_currency("10");
        userData.setMembership_type("高级会员");

        return userData;
    }

    @QueryHandler
    public String query(DynamicEncryptionCodeQuery query) throws Exception {
        String randomString = RandomUtil.randomString(9);
        log.info("动态验证码：" + randomString);
        // 5秒内动态验证码有效！
        redisUtil.set(CommonConstants.REDIS_DYNAMIC_ENCRYPTION_CODE_KEY + query.getIdentification(), randomString, 5);
        return randomString;
    }

    @QueryHandler
    public String query(SmsVerificationCodeQuery query) throws Exception {
        String randomString = RandomUtil.randomNumbers(6);
        // todo 验证码时间要可配置！
        redisUtil.set(CommonConstants.REDIS_SMS_VCODE_KEY + query.getPhone(), randomString, 600);
        log.info("短信验证码：" + randomString);

        // todo 调用发短信接口

        return "下发成功";
    }

    @QueryHandler
    public String query(EmailVerificationCodeQuery query) throws Exception {
        String randomString = RandomUtil.randomNumbers(6);
        log.info("邮件验证码：" + randomString);

        MailDto mailDto = new MailDto();
        mailDto.setTo(query.getEmail());
        mailDto.setSubject("多快出行，邮件注册验证码");
        mailDto.setText("验证码：" + randomString + "\n10分钟内有效，请勿转发！");
        mailService.sendSimpleMailMessage(mailDto);

        // todo 验证码时间要可配置！
        redisUtil.set(CommonConstants.REDIS_EMAIL_VCODE_KEY + query.getEmail(), randomString, 600);

        return "下发成功";
    }

    @QueryHandler
    public boolean query(CheckAccountQuery query) throws Exception {
        log.info("检查账号是否存在");

        // 系统用户查找
        // todo 需要优化！只返回个数就行了，查整个对象太浪费！
        TypedQuery<Passport> namedQuery = getEntityManager()
                .createNamedQuery("Passport.findByAccount", Passport.class);
        namedQuery.setParameter("account", query.getAccount());
        Optional<Passport> optionalPassport = namedQuery.getResultStream().findFirst();

        return optionalPassport.isPresent();
    }

    @QueryHandler
    public boolean query(CheckVerificationCodeQuery query) throws Exception {
        log.info("校验验证码是否正确？：" + query.getCode());
        return true;
    }

}